/**
 * @file
 * @author dousha
 * @date 2020/11/30.
 */

#include <string.h>
#include "menu_state_machine.h"
#include "../Peripherals/eeprom.h"
#include "../Peripherals/oled.h"
#include "../Peripherals/buttons.h"
#include "saved_secrets.h"
#include "constants.h"
#include "../Peripherals/entropy.h"
#include "../Peripherals/flash.h"
#include "convert.h"

static uint8_t state = MENU_FIRST_RUN, count = 0, option = 0;
static uint8_t secretOffset = 0, validSecret = 0, more = 0;
static uint32_t code = 0, lastPressCount = 0;
const static char options[MENU_OPTION_COUNT][8] = {
		"Secrets",
		"System ",
		"Lock   ",
		"RNG    "
};
static uint8_t buf[4] = {0, 0, 0, 0};
static uint8_t inspectorBuffer[32] = {0};

void menu_state_machine_init() {
	oled_clear_screen();
	oled_move_caret(0, 0);
	if (flash_has_setup() > 0) {
		state = MENU_LOCKED;
	} else {
		state = MENU_FIRST_RUN;
	}
	menu_repaint();
}

void menu_draw_dash() {
	oled_clear_screen();
	oled_move_caret(0, 0);
	oled_write_string("Welcome\r\n");
	oled_write_string("\a ");
	oled_write_string(options[option]);
}

void menu_draw_busy() {
	oled_move_caret(3, 0);
	oled_write_string("busy");
}

void menu_clear_busy() {
	oled_move_caret(3, 0);
	oled_write_string("    "); // lol
}

void menu_draw_browser() {
	oled_clear_screen();
	oled_move_caret(0, 0);
	oled_write_string("\a Secrets");
	oled_clear_row(1);
	oled_move_caret(1, 0);
	// FIXME: cannot load entries
	if (secretOffset == 0 && strnlen((const char *) secret_current_get()->name, 16) == 0) {
		oled_write_string("(no secret)");
		validSecret = 0;
	} else {
		oled_write_string((const char *) secret_current_get()->name);
		validSecret = 1;
	}
}

void menu_draw_inspector() {
	oled_clear_screen();
	if (secret_current_get()->metadata & SECRET_META_HEX) {
		// hex display mode
		oled_move_caret(0, 0);
		oled_write_bytes(inspectorBuffer, 8);
		oled_move_caret(1, 0);
		oled_write_bytes(inspectorBuffer + 8, 8);
		oled_move_caret(2, 0);
		oled_write_bytes(inspectorBuffer + 16, 8);
		oled_move_caret(3, 0);
		oled_write_bytes(inspectorBuffer + 24, 8);
	} else {
		// alpha display mode
		oled_move_caret(0, 0);
		oled_write_length_string((const char *) secret_current_get()->name, 16);
		oled_move_caret(1, 0);
		oled_write_length_string((const char *) inspectorBuffer, 16);
		oled_move_caret(2, 0);
		oled_write_length_string((const char *) inspectorBuffer + 16, 16);
	}
	if (more) {
		oled_move_caret(3, 17);
		oled_write_string("\a");
	}
}

void menu_draw_rng() {
	oled_move_caret(0, 0);
	oled_write_string("\a Random Generator\r\nPress middle button");
	oled_move_caret(2, 0);
	uint8_t rand = entropy_read();
	write_u8(buf, rand);
	oled_write_string((const char *) buf);
}

void menu_draw_info() {
	oled_move_caret(0, 0);
	oled_write_string("\a System info\r\n");
	oled_write_string(FIRMWARE_VERSION);
	oled_write_string("Boot count: ");
	write_u8(buf, eeprom_get_startup_count());
	oled_write_string((const char *) buf);
}

void menu_prev_secret_entry() {
	if (!validSecret) {
		return;
	}
	if (secretOffset == 0) {
		for (uint8_t i = SECRET_INDEX_TABLE_SIZE; i > 0; i++) {
			if (secret_index_is_valid(i)) {
				secretOffset = i;
			}
		}
	} else {
		--secretOffset;
	}
}

void menu_next_secret_entry() {
	if (!validSecret) {
		return;
	}
	++secretOffset;
	if (!secret_index_is_valid(secretOffset)) {
		secretOffset = 0;
	}
}

void menu_secret_examine() {
	if (!validSecret) {
		state = MENU_BROWSER;
		return;
	}
	secret_get_prepare(secretOffset);
	more = secret_read_page(inspectorBuffer);
}

void menu_secret_advance_block() {
	more = secret_read_page(inspectorBuffer);
}

void menu_repaint() {
	switch (state) {
		case MENU_LOCKED:
			oled_clear_screen();
			oled_move_caret(0, 0);
			oled_write_string("Button combination?");
			oled_move_caret(1, 0);
			oled_write_string("> ");
			break;
		case MENU_FIRST_RUN:
			oled_clear_screen();
			oled_write_string("Please input 6 button combinations to setup > ");
			break;
		case MENU_DASH:
			menu_draw_dash();
			break;
		case MENU_BROWSER:
			menu_draw_browser();
			break;
		case MENU_INSPECTOR:
			menu_draw_inspector();
			break;
		case MENU_RNG:
			menu_draw_rng();
			break;
		case MENU_INFO:
			menu_draw_info();
			break;
		case MENU_ACCESS_DENIED:
			oled_clear_screen();
			oled_move_caret(0, 0);
			oled_write_string("Bad password!\r\nDevice locked.");
			break;
		default:
			break;
	}
	oled_update_screen();
}

void menu_state_machine_callback(uint8_t s) {
	uint8_t preventRedraw = 0;
	switch (state) {
		case MENU_LOCKED:
		case MENU_FIRST_RUN:
			preventRedraw = 1;
			// submit a combination
			code |= s;
			code <<= 4u;
			oled_write_string("*");
			oled_update_screen(); // XXX: partial update?
			if (++count >= 6) {
				// submit verification
				preventRedraw = 0;
				if (state == MENU_LOCKED) {
					if (secret_unlock(code)) {
						count = 0;
						state = MENU_DASH;
					} else {
						state = MENU_ACCESS_DENIED;
					}
				} else {
					secret_generate(code);
					flash_finish_setup();
					if (!flash_has_setup()) {
						state = MENU_ACCESS_DENIED;
					} else {
						state = MENU_DASH;
					}
				}
			}
			break;
		case MENU_DASH:
			if (s == BUTTON_LEFT) {
				if (option == 0) {
					option = MENU_OPTION_COUNT - 1;
				} else {
					option--;
				}
			} else if (s == BUTTON_RIGHT) {
				if (++option >= MENU_OPTION_COUNT) {
					option = 0;
				}
			} else if (s == BUTTON_MIDDLE) {
				switch (option) {
					case 0:
						state = MENU_BROWSER;
						secretOffset = 0;
						break;
					case 1:
						state = MENU_INFO;
						break;
					case 2:
						state = MENU_LOCKED;
						break;
					case 3:
						state = MENU_RNG;
						break;
					default:
						option = 0;
						break;
				}
			}
			break;
		case MENU_INFO:
			if (s != BUTTON_NONE) {
				state = MENU_DASH;
			}
			break;
		case MENU_RNG:
			if (s == BUTTON_LEFT) {
				state = MENU_DASH;
			}
			break;
		case MENU_BROWSER:
			if (s == BUTTON_LEFT_AND_RIGHT) {
				state = MENU_DASH;
			} else {
				switch (s) {
					case BUTTON_LEFT:
						menu_prev_secret_entry();
						break;
					case BUTTON_RIGHT:
						menu_next_secret_entry();
						break;
					case BUTTON_MIDDLE:
						state = MENU_INSPECTOR;
						menu_secret_examine();
						break;
					default:
						break;
				}
			}
			break;
		case MENU_INSPECTOR:
			if (s == BUTTON_LEFT) {
				state = MENU_BROWSER;
			} else if (s == BUTTON_MIDDLE || s == BUTTON_RIGHT) {
				if (more) {
					menu_secret_advance_block();
				} else {
					state = MENU_BROWSER;
				}
			}
			break;
		default:
			break;
	}
	if (!preventRedraw) {
		menu_repaint();
	}
}

uint8_t is_device_unlocked() {
	return state != MENU_FIRST_RUN && state != MENU_LOCKED && state != MENU_ACCESS_DENIED;
}
